package com.kaibes.web.encryption;

import java.io.UnsupportedEncodingException;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.Map;

import javax.crypto.BadPaddingException;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.servlet.http.HttpServletRequest;

import org.springframework.stereotype.Component;

import com.kaibes.core.base.util.AesUtils;
import com.kaibes.core.base.util.RsaUtils;
import com.kaibes.core.base.util.StringUtils;
import com.kaibes.core.spring.proxy.Proxy;

@Component
public class EncryptionUtils {

	@Proxy
	private EncryptionKeyService encryptionKeyService;
	private ThreadLocal<EncryptionKey> threadLocal = new ThreadLocal<>();

	public static RsaKey createRsaKey() {
		try {
			Map<String, Object> pair = RsaUtils.genKeyPair();
			String privateKey = RsaUtils.getPrivateKey(pair);
			String publicKey = RsaUtils.getPublicKey(pair);

			RsaKey rsaKey = new RsaKey();
			rsaKey.setPrivateKey(privateKey);
			rsaKey.setPublicKey(publicKey);
			return rsaKey;
		} catch (Exception e) {
			throw new RuntimeException(e);
		}
	}

	public static AesKey createAesKey() {
		AesKey aesKey = new AesKey();
		aesKey.setKey(StringUtils.random(16));
		aesKey.setIv(StringUtils.random(16));
		return aesKey;
	}

	private EncryptionKey getKey() {
		EncryptionKey key = threadLocal.get();
		if (key == null) {
			key = encryptionKeyService.getObject(EncryptionKey.class);
			threadLocal.set(key);
		}
		if (key == null) {
			key = new EncryptionKey();
			key.setAesKey(createAesKey());
			key.setRsaKey(createRsaKey());
			encryptionKeyService.saveObject(key);
			threadLocal.set(key);
		}
		return key;
	}

	// =====================
	public String getRsaPublicKey() {
		return getKey().getRsaKey().getPublicKey();
	}

	public String getRsaPrivateKey() {
		return getKey().getRsaKey().getPrivateKey();
	}

	public String getRsaPublicKey(HttpServletRequest request) {
		Object publicKey = request.getAttribute(EncryptionRes.RSA_PUBLIC_KEY);
		if (publicKey == null) {
			publicKey = request.getSession().getAttribute(EncryptionRes.RSA_PUBLIC_KEY);
		}
		return (String) publicKey;
	}

	public String getRsaPrivateKey(HttpServletRequest request) {
		Object privateKey = request.getAttribute(EncryptionRes.RSA_PRIVATE_KEY);
		if (privateKey == null) {
			privateKey = request.getSession().getAttribute(EncryptionRes.RSA_PRIVATE_KEY);
		}
		return (String) privateKey;
	}

	// ==========================
	public String getAesKey() {
		return getKey().getAesKey().getKey();
	}

	public String getAesIv() {
		return getKey().getAesKey().getIv();
	}

	public String getAesKey(HttpServletRequest request) {
		Object aesKey = request.getAttribute(EncryptionRes.AES_KEY);
		if (aesKey == null) {
			aesKey = request.getSession().getAttribute(EncryptionRes.AES_KEY);
		}
		return (String) aesKey;
	}

	public String getAesIv(HttpServletRequest request) {
		Object aesIv = request.getAttribute(EncryptionRes.AES_IV);
		if (aesIv == null) {
			aesIv = request.getSession().getAttribute(EncryptionRes.AES_IV);
		}
		return (String) aesIv;
	}

	// ================
	public String decrypt2String(String content) throws InvalidKeyException, NoSuchAlgorithmException,
			NoSuchPaddingException, InvalidAlgorithmParameterException, IllegalBlockSizeException, BadPaddingException {
		if (StringUtils.isBlank(getAesIv())) {
			return AesUtils.ECB.decrypt2String64(content, getAesKey());
		} else {
			return AesUtils.CBC.decrypt2String64(content, getAesKey(), getAesIv());
		}
	}

	public String decrypt2String(String content, HttpServletRequest request)
			throws InvalidKeyException, NoSuchAlgorithmException, NoSuchPaddingException,
			InvalidAlgorithmParameterException, IllegalBlockSizeException, BadPaddingException {
		String key = getAesKey(request);
		String iv = getAesIv(request);
		if (StringUtils.isBlank(iv)) {
			return AesUtils.ECB.decrypt2String64(content, key);
		} else {
			return AesUtils.CBC.decrypt2String64(content, key, iv);
		}
	}

	public String encrypt2String(String content)
			throws InvalidKeyException, NoSuchAlgorithmException, NoSuchPaddingException, IllegalBlockSizeException,
			BadPaddingException, UnsupportedEncodingException, InvalidAlgorithmParameterException {
		if (StringUtils.isBlank(getAesIv())) {
			return AesUtils.ECB.encrypt2String64(content, getAesKey());
		} else {
			return AesUtils.CBC.encrypt2String64(content, getAesKey(), getAesIv());
		}
	}

	public String encrypt2String(String content, HttpServletRequest request)
			throws InvalidKeyException, NoSuchAlgorithmException, NoSuchPaddingException, IllegalBlockSizeException,
			BadPaddingException, UnsupportedEncodingException, InvalidAlgorithmParameterException {
		String key = getAesKey(request);
		String iv = getAesIv(request);
		if (StringUtils.isBlank(iv)) {
			return AesUtils.ECB.encrypt2String64(content, key);
		} else {
			return AesUtils.CBC.encrypt2String64(content, key, iv);
		}
	}

}
